// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_WEBSOCKETS_WEBSOCKET_DEFLATE_PARAMETERS_H_
#define NET_WEBSOCKETS_WEBSOCKET_DEFLATE_PARAMETERS_H_

#include <stdint.h>

#include <string>

#include "base/logging.h"
#include "net/base/net_export.h"
#include "net/websockets/websocket_deflater.h"
#include "net/websockets/websocket_extension.h"

namespace net {

// A WebSocketDeflateParameters represents permessage-deflate extension
// parameters. This class is used either for request and response.
class NET_EXPORT_PRIVATE WebSocketDeflateParameters {
public:
    using ContextTakeOverMode = WebSocketDeflater::ContextTakeOverMode;

    // Returns a WebSocketExtension instance containing the parameters stored in
    // this object.
    WebSocketExtension AsExtension() const;

    // Returns true when succeeded.
    // Returns false and stores the failure message to |failure_message|
    // otherwise.
    // Note that even if this function succeeds it is not guaranteed that the
    // object is valid. To check it, call IsValidAsRequest or IsValidAsResponse.
    bool Initialize(const WebSocketExtension& input,
        std::string* failure_message);

    // Returns true when |*this| and |response| are compatible.
    // |*this| must be valid as a request and |response| must be valid as a
    // response.
    bool IsCompatibleWith(const WebSocketDeflateParameters& response) const;

    bool IsValidAsRequest(std::string* failure_message) const;
    bool IsValidAsResponse(std::string* failure_message) const;
    bool IsValidAsRequest() const
    {
        std::string message;
        return IsValidAsRequest(&message);
    }
    bool IsValidAsResponse() const
    {
        std::string message;
        return IsValidAsResponse(&message);
    }

    ContextTakeOverMode server_context_take_over_mode() const
    {
        return server_context_take_over_mode_;
    }
    ContextTakeOverMode client_context_take_over_mode() const
    {
        return client_context_take_over_mode_;
    }
    bool is_server_max_window_bits_specified() const
    {
        return server_max_window_bits_.is_specified;
    }
    int server_max_window_bits() const
    {
        DCHECK(is_server_max_window_bits_specified());
        return server_max_window_bits_.bits;
    }
    bool is_client_max_window_bits_specified() const
    {
        return client_max_window_bits_.is_specified;
    }
    bool has_client_max_window_bits_value() const
    {
        DCHECK(is_client_max_window_bits_specified());
        return client_max_window_bits_.has_value;
    }
    int client_max_window_bits() const
    {
        DCHECK(has_client_max_window_bits_value());
        return client_max_window_bits_.bits;
    }
    void SetServerNoContextTakeOver()
    {
        server_context_take_over_mode_ = WebSocketDeflater::DO_NOT_TAKE_OVER_CONTEXT;
    }
    void SetClientNoContextTakeOver()
    {
        client_context_take_over_mode_ = WebSocketDeflater::DO_NOT_TAKE_OVER_CONTEXT;
    }
    // |bits| must be valid as a max_window_bits value.
    void SetServerMaxWindowBits(int bits)
    {
        DCHECK(IsValidWindowBits(bits));
        server_max_window_bits_ = WindowBits(bits, true, true);
    }
    void SetClientMaxWindowBits()
    {
        client_max_window_bits_ = WindowBits(0, true, false);
    }
    // |bits| must be valid as a max_window_bits value.
    void SetClientMaxWindowBits(int bits)
    {
        DCHECK(IsValidWindowBits(bits));
        client_max_window_bits_ = WindowBits(bits, true, true);
    }

    int PermissiveServerMaxWindowBits() const
    {
        return server_max_window_bits_.PermissiveBits();
    }
    int PermissiveClientMaxWindowBits() const
    {
        return client_max_window_bits_.PermissiveBits();
    }

    // Return true if |bits| is valid as a max_window_bits value.
    static bool IsValidWindowBits(int bits) { return 8 <= bits && bits <= 15; }

private:
    struct WindowBits {
        WindowBits()
            : WindowBits(0, false, false)
        {
        }
        WindowBits(int16_t bits, bool is_specified, bool has_value)
            : bits(bits)
            , is_specified(is_specified)
            , has_value(has_value)
        {
        }

        int PermissiveBits() const
        {
            return (is_specified && has_value) ? bits : 15;
        }

        int16_t bits;
        // True when "window bits" parameter appears in the parameters.
        bool is_specified;
        // True when "window bits" parameter has the value.
        bool has_value;
    };

    // |server_context_take_over_mode| is set to DO_NOT_TAKE_OVER_CONTEXT if and
    // only if |server_no_context_takeover| is set in the parameters.
    ContextTakeOverMode server_context_take_over_mode_ = WebSocketDeflater::TAKE_OVER_CONTEXT;
    // |client_context_take_over_mode| is set to DO_NOT_TAKE_OVER_CONTEXT if and
    // only if |client_no_context_takeover| is set in the parameters.
    ContextTakeOverMode client_context_take_over_mode_ = WebSocketDeflater::TAKE_OVER_CONTEXT;
    WindowBits server_max_window_bits_;
    WindowBits client_max_window_bits_;
};

} // namespace net

#endif // NET_WEBSOCKETS_WEBSOCKET_DEFLATE_PARAMETERS_H_
